<?php

namespace App\Services;

use App\Exceptions\ApiException;
use App\Models\BookTitleMerge;
use App\Models\PartCompany;
use App\Models\ProjectClassify;
use Carbon\Carbon;

/**
 * 案卷目录 服务层类
 * Class BookTitleService
 * @package App\Services
 */
class BookTitleMergeService extends BaseService
{
    public $bookCatalogService;

    public function __construct(BookCatalogService $bookCatalogService)
    {
        parent::__construct();
        $this->bookCatalogService = $bookCatalogService;
    }

    /**
     * 获取可能要关联的参见单位
     * @author: lxk
     * @date: 2023/6/26 16:26
     */
    public function getPartCompanies($partCompanyId, $projectClassifyId)
    {
        $projectClassify = ProjectClassify::query()->find($projectClassifyId);
        $archivesNo      = $projectClassify->archives_no;
        $partCompanyIds  = ProjectClassify::query() //全部可能关联的参见单位
            ->where('archives_no', $archivesNo)
            ->where('id', '!=', $projectClassifyId)
            ->pluck('part_company_id')
            ->toArray();
        $info = PartCompany::query()->find($partCompanyId);
        $list = PartCompany::query()
            ->where('project_id', $info->project_id) //同一项目下
            ->where('type', $info->type) //相同类型
            ->whereIn('id', $partCompanyIds)
            ->select('id', 'title')
            ->get();

        if ($list->isEmpty()) return [];

        $list = $list->toArray();

        $hadMergePartCompanyIds = BookTitleMerge::query()->where('archives_no', $archivesNo)->pluck('part_company_id');
        $hadMergePartCompanyIds = $hadMergePartCompanyIds->isNotEmpty() ? $hadMergePartCompanyIds->toArray() :  [];
        foreach ($list as &$val) {
            $val['select'] = 0;
            if (in_array($val['id'], $hadMergePartCompanyIds)) {
                $val['select'] = 1;
            }
        }
        return $list;
    }

    /**
     * 关联参见单位
     */
    public function relation($projectId, $projectClassifyId, $partCompanyIds)
    {
        $projectClassify = ProjectClassify::query()->find($projectClassifyId);
        $archivesNo = $projectClassify->archives_no;
        array_push($partCompanyIds, $projectClassify->part_company_id); //选择的加上当前自己的

        $list = ProjectClassify::query()
            ->select('id as project_classify_id', 'part_company_id', 'archives_no')
            ->where('archives_no', $archivesNo)
            ->whereIn('part_company_id', $partCompanyIds)
            ->get()
            ->toArray();

        BookTitleMerge::query()->where(['project_id'=> $projectId, 'archives_no' => $archivesNo])->delete();
        foreach ($list as $val) {
            BookTitleMerge::query()->updateOrCreate([
                    'project_id'          => $projectId,
                    'archives_no'         => $archivesNo,
                    'project_classify_id' => $val['project_classify_id'],
                    'part_company_id'     => $val['part_company_id'],
                ],[
                    $data['creator_id'] = user_id(),
                    $data['created_at'] = Carbon::now()->toDateTimeString(),
                    $data['updated_at'] = Carbon::now()->toDateTimeString(),
                ]);
        }
        CommonService::addProjectActionLog('关联参见单位', $projectClassifyId, $partCompanyIds[0], $projectClassifyId);

        return true;
    }

    /**
     * 判断是否有关联关系
     * @author: lxk
     * @date: 2023/6/27 14:51
     */
    public static function isHaveMerge($projectClassifyId)
    {
        return BookTitleMerge::query()->where('project_classify_id', $projectClassifyId)->first();
    }

    /**
     * 执行合并案卷，同时合并卷内
     */
    public function mergeAction($projectClassifyId)
    {
        $bookTitleMerge = self::isHaveMerge($projectClassifyId);
        if (empty($bookTitleMerge)) {
            throw new ApiException('查不到关联信息，无法合并');
        }
        $this->isAllowMerge($projectClassifyId); //判断是否允许合并

        $mergeInfo = self::getMergeInfo($projectClassifyId);

        $redisKey1     = get_rand_str('can_merge'); //定义临时可以合并的案卷
        $redisKey2     = get_rand_str('not_merge'); //不能合并的案卷
        $afterMergeArr = [];
        //1、先遍历循环，取待合并单位各自的有效案卷构造一个【合并后数据数组】
        foreach ($mergeInfo['mergers'] as $mergers) {
            $bookTitleInstance = $this->getBookTitleInstance($mergers['part_company_id'], $mergers['project_classify_id']);
            $list = $bookTitleInstance
                ->where('title', '!=', '等待合并') //正常案卷
                ->where('part_company_id', $mergers['part_company_id'])
                ->orderBy('serial_no')
                ->get()
                ->toArray();
            foreach ($list as $val) {
                if (redis()->sadd($redisKey1, $val['serial_no'])) { //加入集合，保证不重复
                    $afterMergeArr[] = $val;
                } else { //说明一个案卷被多个参建单位做了，记下来，忽略合并，提示用户
                    redis()->sadd($redisKey2, $val['serial_no']);
                }
            }
        }
        //2、循环【合并后数据数组】，分别让各个参见单位【等待合并】的案卷与之比对
        foreach ($mergeInfo['mergers'] as $mergers) {
            $bookTitleInstance = $this->getBookTitleInstance($mergers['part_company_id'], $mergers['project_classify_id']);
            $waitMergeList = $bookTitleInstance
                ->where('title', '=', '等待合并') //等待合并案卷
                ->select('id', 'serial_no', 'part_company_id', 'project_classify_id')
                ->orderBy('serial_no')
                ->get()
                ->toArray();
            //第三步 合并卷内数据
            $this->mergeCatalogs($afterMergeArr, $waitMergeList);

            BookTitleMerge::query()->where('id', $mergers['id'])->update(['status' => 2]);
        }
        $repeats = redis()->smembers($redisKey2); //获取被重复做的案卷序号，返回数组
        $notice  = '';
        if (!empty($repeats)) { //需要提示有案卷被重复做了
            $serialNos = implode(',', $repeats);
            $notice    = "序号为【{$serialNos}】的案卷重复做了，忽略合并！请记下序号并修正！";
        }
        redis()->del($redisKey1);
        redis()->del($redisKey2);

        $projectClassify = ProjectClassify::query()->find($projectClassifyId);
        $archivesNo = $projectClassify->archives_no;

        BookTitleMerge::query() ->where('archives_no', $archivesNo)->increment('merge_count', 1);//合并次数加1

        $projectClassify = ProjectClassify::query()->find($projectClassifyId);
        CommonService::addProjectActionLog('合并案卷', $projectClassifyId, $projectClassify->part_company_id, $projectClassifyId);

        return $notice;
    }

    /**
     * 同时合并卷内数据
     */
    protected function mergeCatalogs($afterMergeArr, $waitMergeList)
    {
        foreach ($afterMergeArr as $val1) { //【合并后数据数组】
            foreach ($waitMergeList as $val2) { //待合并案卷
                if ($val1['serial_no'] == $val2['serial_no']) {
                    $bookTitleId = $val1['id']; //暂存起来（非合并案卷）
                    unset($val1['id']); //原案卷id，不要
                    $bookTitleInstance2 = $this->getBookTitleInstance($val2['part_company_id'], $val2['project_classify_id']);
                    $bookTitleInstance2->where('id', $val2['id'])->update($val1); //更新案卷
                    $bookCatalogInstance1 = $this->getBookCatalogInstance($val1['part_company_id'], $val1['project_classify_id']);
                    $list = $bookCatalogInstance1 //查出有效案卷的卷内数据
                        ->where('book_title_id', $bookTitleId)
                        ->select('serial_no', 'title', 'file_number', 'director', 'creator_id', 'c_date', 'page_num', 'is_pic', 'file_url', 'remark')
                        ->get()
                        ->toArray();
                    foreach ($list as &$v) {
                        $v['book_title_id'] = $val2['id']; //注意！案卷id是待合并案卷id
                    }
                    $bookCatalogInstance2 = $this->getBookCatalogInstance($val2['part_company_id'], $val2['project_classify_id']);
                    $bookCatalogInstance2->insert($list);
                }
            }
        }
    }

    /**
     * 判断是否允许合并
     * 满足：序号一致、档案号一致、案卷正确、卷内正确、没有空案卷
     */
    public function isAllowMerge($projectClassifyId)
    {
        $bookTitleMerge = self::isHaveMerge($projectClassifyId);
        $partCompanyIds = to_array($bookTitleMerge->part_company_ids);
        $redisKey       = get_rand_str('is_allow_merge');
        foreach ($partCompanyIds as $partCompanyId) {
            $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
            $list = $bookTitleInstance->select('serial_no', 'archives_no', 'is_have_data', 'problem')->get();
            $partCompany = PartCompany::query()->where('id', $partCompanyId)->value('title');
            if ($list->isEmpty()) {
                throw new ApiException("禁止合并!案卷为空,【{$partCompany}】");
            }
            $list          = $list->toArray();
            $serialNosArr  = array_column($list, 'serial_no'); //获取序号
            $archivesNoArr = array_column($list, 'archives_no'); //获取档案号
            $isHaveDataArr = array_column($list, 'is_have_data'); //案卷是否为空
            $problem       = array_column($list, 'problem'); //异常案卷
            if (redis()->sadd($redisKey, $serialNosArr)) { //如果可以加入集合，说明序号值不一致
                throw new ApiException("禁止合并！序号值不一致,请补齐案卷，【{$partCompany}】");
            }
            if (redis()->sadd($redisKey, $archivesNoArr)) { //如果可以加入集合，说名档案号不一致
                throw new ApiException("禁止合并！档案号不一致,请补齐案卷或格式化档案号，【{$partCompany}】");
            }
            if (in_array('0', $isHaveDataArr)) {
                throw new ApiException("禁止合并！存在空案卷，【{$partCompany}】");
            }
            if (!empty($problem)) {
                throw new ApiException("禁止合并！存在异常案卷，【{$partCompany}】");
            }
        }
        redis()->del($redisKey);
    }

    /**
     * 补齐案卷
     */
    public function completionBootTitle($partCompanyId, $projectClassifyId, $totalCount)
    {
        $info = self::getMergeInfo($projectClassifyId);
        if (!empty($info['mergers'])) { //有关联信息
            foreach ($info['mergers'] as $val) {
                $this->autoFillOne($val['part_company_id'], $val['project_classify_id'], $totalCount);
            }
        } else { //只补齐自己
            $this->autoFillOne($partCompanyId, $projectClassifyId, $totalCount);
        }

        return true;
    }

    /**
     * 自动补齐一个单位工程的案卷
     * 这个功能是为了方便“案卷合并”的，如果案卷总数 1250份，A、B、C三个参见单位共同做，导入的案卷原本只有本单位负责的那部分案卷
     * 为了保持数据档案号的连续性，自动补齐案卷得到最大案卷值之后，会自动为三家单位补齐空缺的案卷，案卷标题为 “等待合并”
     */
    public function autoFillOne($partCompanyId, $projectClassifyId, $totalCount)
    {
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $info = $bookTitleInstance->first();
        if (empty($info)) {
            throw new ApiException('案卷为空！至少需要有一个案卷');
        }
        $mainArchivesNo = substr($info->archives_no, 0, strrpos($info->archives_no, '-')); //strrpos()获取字符串最后一次出现的位置
        $maxSerialNo    = $bookTitleInstance->max('serial_no');
        if ($totalCount < $maxSerialNo) {
            throw new ApiException('已存在的最大案卷序号大于您输入的案卷总数！');
        }
        $redisKey = get_rand_str('auto_fill');
        redis()->set($redisKey, 0);

        $minSerialNoInfo = $bookTitleInstance->where('serial_no', 1)->first();
        if (empty($minSerialNoInfo)) { //写入序号最小的那个案卷，方便后续插入数据
            $bookTitleInstance->insert([
                'company_id'          => company_id(),
                'part_company_id'     => $partCompanyId,
                'project_classify_id' => $projectClassifyId,
                'serial_no'           => 1,
                'archives_no'         => $mainArchivesNo . '-1',
                'title'               => '等待合并',
                'creator_id'          => user_id(),
            ]);
            redis()->incrby($redisKey, 1);
        }
        $maxSerialNoInfo = $bookTitleInstance->where('serial_no', $totalCount)->first();
        if (empty($maxSerialNoInfo)) { //写入序号最大的那个案卷，方便后续插入数据
            $bookTitleInstance->insert([
                'company_id'          => company_id(),
                'part_company_id'     => $partCompanyId,
                'project_classify_id' => $projectClassifyId,
                'serial_no'           => $totalCount,
                'archives_no'         => $mainArchivesNo . '-' . $totalCount,
                'title'               => '等待合并',
                'creator_id'          => user_id(),
            ]);
            redis()->incrby($redisKey, 1);
        }
        $list = $bookTitleInstance
            ->select('id', 'serial_no', 'archives_no')
            ->orderBy('serial_no')
            ->get()
            ->toArray();

        foreach ($list as $key => $val) {
            if ($key > 0) {
                $nowSerialNo = $val['serial_no'];
                $preSerialNo = $list[$key - 1]['serial_no'];
                if ($nowSerialNo - $preSerialNo > 1) { //检测序号是否连续
                    $insertCount    = $nowSerialNo - $preSerialNo - 1;  //插入几条数据
                    $archivesNo     = $info->archives_no;
                    $mainArchivesNo = substr($archivesNo, 0, strrpos($archivesNo, '-')); //strrpos()获取字符串最后一次出现的位置
                    $batchData      = []; //构造一个批量插入数组
                    for ($i = 0; $i < $insertCount; $i++) {
                        redis()->incrby($redisKey, 1);
                        $newSerialNo    = $nowSerialNo - $i - 1;
                        $newArchivesNo  = $mainArchivesNo . '-' . $newSerialNo;
                        $batchData[$i]['company_id']          = company_id();
                        $batchData[$i]['part_company_id']     = $partCompanyId;
                        $batchData[$i]['project_classify_id'] = $projectClassifyId;
                        $batchData[$i]['serial_no']           = $newSerialNo;
                        $batchData[$i]['archives_no']         = $newArchivesNo;
                        $batchData[$i]['title']               = '等待合并';
                        $batchData[$i]['creator_id']          = user_id();
                        $batchData[$i]['created_at']          = now();
                        $batchData[$i]['updated_at']          = now();
                    }
                    $bookTitleInstance->insert($batchData);
                }
            }
        }
        $count = redis()->get($redisKey);
        redis()->del($redisKey); //用完清除

        CommonService::addProjectActionLog('补齐案卷', 0, $partCompanyId, $projectClassifyId);

        return $count;
    }

    /**
     * 获取可以合并的案卷
     * @author: lxk
     * @date: 2023/6/24 18:06
     */
    public function getMerges($projectClassifyId, $dataCount)
    {
        $bookTitleMerge = self::isHaveMerge($projectClassifyId); //第一步先看看有没有关联数据
        if (empty($bookTitleMerge)) return [];
        $projectClassify = ProjectClassify::query()->find($projectClassifyId);
        $archivesNo = $projectClassify->archives_no;
        $list = BookTitleMerge::query()
            ->select('id', 'archives_no', 'project_classify_id', 'part_company_id', 'status')
            ->where('archives_no', $archivesNo)
            ->get()
            ->toArray();
        $data = [];
        foreach ($list as $val) {
            $bookTitleInstance = $this->getBookTitleInstance($val['part_company_id'], $val['project_classify_id']);
            $tmp['id']                  = $val['part_company_id'];
            $tmp['part_company']        = PartCompany::query()->where('id', $val['part_company_id'])->value('title');
            $tmp['project_classify_id'] = $projectClassifyId;
            $tmp['project_classify']    = $projectClassify->title;
            $tmp['book_title_count']    = $bookTitleInstance->count();
            $tmp['status']              = $val['status'];
            $tmp['notice']              = ($tmp['book_title_count'] == $dataCount) ? '数量一致' : '数量不一致';
            $data[]                     = $tmp;
        }
        return $data;
    }

    /**
     * 获取合并案卷关联信息
     * @author: lxk
     * @date: 2023/12/22 16:43
     */
    public static function getMergeInfo($projectClassifyId)
    {
        $projectClassify = ProjectClassify::query()->find($projectClassifyId);
        $archivesNo = $projectClassify->archives_no;
        $list = BookTitleMerge::query()
            ->where('archives_no', $archivesNo)
            ->select('id', 'archives_no', 'part_company_id', 'project_classify_id', 'merge_count')
            ->get();
        return [
            'archives_no' => $archivesNo,
            'mergers'     => $list->isNotEmpty() ? $list->toArray() : [],
        ];
    }


}
